
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.Text;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;

namespace Approval
{
	public class BizRules : Syrinx.BaseBizRules
	{
		public IBizObjList getApprovalHistoryView(IApplication app, IRuleContext context, Swaf.BGuid.Guid objectId)
		{
			//return app.bizObjMgr.createList("ApprovalDataChange");
			if (objectId == null)
                throw new ApprovalException("Error executing getApprovalHistoryView 'theBizObj' parameter is null");
            try
            {
				context.put("objectId", objectId);
                IBizObjList lst =app.appData.get("DA.Approval.GetBizObjApprovalHistory", context) as  IBizObjList;
                return lst;
            }
            catch(Exception e)
            {
                throw new ApprovalException("Error executing getApprovalHistoryView",e);
            }
		}


		public void executeApprovalAction(IApplication app, IRuleContext context, IBizObj bizObjData,ApprovalActionState action)
		{
			try
			{
				if (bizObjData != null  )
				{
					IResults rc = app.ruleMgr.execute(context, "getApprovalDetails", bizObjData);
					if (rc.ExceptionInfo != null)
						throw new Exception("error executing getApprovalDetails on ExecuteApproval bizrule", rc.ExceptionInfo);
					
					IBizObj approvalData = rc.CallResults as IBizObj;
					IBizObj approvalSet = approvalData.get("approvalSet") as IBizObj;
					string actionType=Enum.GetName(typeof(ApprovalActionState), action);
					
					rc = app.ruleMgr.execute("getNextApprover", approvalSet);
					
					if (rc.ExceptionInfo != null)
						throw new Exception("error executing calcNextAPprover on executeApprovalAction:" + actionType);
					
					Swaf.BGuid.Guid nextApprover = rc.CallResults as Swaf.BGuid.Guid;
					FlexiMap m = new FlexiMap();
					m["approvalDataId"] = approvalData.get("id");
					m["nextApprover"] = nextApprover != null ? nextApprover : CurrentUser.get("id");
					m["createdBy"] = CurrentUser.get("id");
					m["createdDate"] = DateTime.Now;
					m["originalAction"] = actionType;
					//m["dataChangeId"] = approvalDataChange.get("id");
					m["bizObjData"] = bizObjData.getXml("all setOnly");

					m["state"] = (action == ApprovalActionState.Approve && nextApprover != null) ? approvalData.get("state") : action;

					if (action == ApprovalActionState.Rollback)
					{
						m["state"] = ApprovalActionState.ForApproval;
						if ("1".Equals(approvalSet.get("state").ToString()))
							context.put("RollBackAction", 1);
					}
					
					app.appData.put("DA.Approval.PostApprovalVote", m);
					if ((bool)approvalData.get("approvalSet.saveHistory") == true)
					{
						m["id"] = new Swaf.BGuid.Guid();
						app.appData.put("DA.Approval.InsertApprovalDataChange", m);
					}
					else
						app.appData.put("DA.Approval.UpdateApprovalDataChange", m);
					context.put("actionType", m["originalAction"]);

					if (nextApprover == null && context.get("PostRuleExecutionRollBackCall") == null && action != ApprovalActionState.Denied)
					{
						context.put("RollBackAction", null);
						context.put("PostRuleExecutionRollBackCall", null);
						string strObj = approvalData.get("rawbizObj").ToString();
						IBizObjSource src = app.bizObjMgr.createSource(SourceType.BasicSource, strObj) as IBizObjSource;
						IBizObj thebizobj = src.getList(approvalSet.get("objectType").ToString(), null)[0];
						thebizobj.put("approvalDataId", approvalData.get("id"));
						rc = app.ruleMgr.execute(context, approvalData.get("originalAction").ToString(), thebizobj);
						if (rc.ExceptionInfo != null)
							throw new Exception(string.Format("error executing {0}", approvalData.get("originalAction")), rc.ExceptionInfo);
					}
					else
						if (context.get("RollBackAction") != null)
						{
							//TODO:
						}
				}
			}
			catch (Exception e)
			{
				throw new ApprovalException("error executing executeApprove bizrule.", e);
			}
		}

		
		public string getParentFieldName(IApplication app, IRuleContext context,string objectTypeName)
		{
			IApprovalManager mgr = app.appData["sysObjs-Approval"] as IApprovalManager;
			IBizObj info = mgr.ApprovalInfos.search("id",objectTypeName);
			if (info != null)
				return info.get("parentFieldId").ToString();
			return null;

		}
		public IBizObj checkForCompanyApproval(IApplication app, IRuleContext context)
		{
			try
			{
				if (CurrentCompanyId != null)
				{
					IBizObj company = app.bizObjMgr.create("Company");
					company.put("id", CurrentCompanyId);
					IBizObjList l = app.appData.get("DA.Approval.GetCompanyApprovalFeature", company) as IBizObjList;
						if (l.numElements > 0)
							return l[0];
				}
				return null;
			}
			catch (Exception e)
			{
				throw new ApprovalException("error executing checkForCOmpanyApproval bizrule.", e);
			}
			
		}
		public IBizObj getApprovalDetails(IApplication app, IRuleContext context,IBizObj bizObj)
		{
			try
			{
				IBizObj approvalData = app.bizObjMgr.create("ApprovalData");
				approvalData.put("state",(int)ApprovalActionState.NoApproval);
				if (bizObj != null && bizObj.containsField("approvalDataId"))
				{
					IApprovalManager mngr = app.appData["sysObjs-Approval"] as IApprovalManager;
					if (mngr != null)
					{
						IBizObj appInfo = mngr.ApprovalInfos.search("id", bizObj.type.Name);
						FlexiMap m = new FlexiMap();

						m["objectId"] = appInfo != null ? bizObj.get(appInfo.get("parentFieldId").ToString()) : bizObj.get("id");
						m["typeName"] = bizObj.type.Name;
						IBizObjList list = app.appData.get("DA.Approval.GetApprovalDataByObjectId", m) as IBizObjList;
						if (list.numElements > 0)
						{
							IBizObjList approvers = app.bizObjMgr.createList("Approvers");
							IResults rc = app.ruleMgr.execute(context, "getApprovers", list[0].get("approvalSet") as IBizObj);
							if (rc.ExceptionInfo == null && rc.CallResults is IBizObjList)
								approvers = rc.CallResults as IBizObjList;
							bool userIsApprover = calcUserIsPartOfApproval(approvers);
							foreach (IBizObj obj in list)
							{
								if (hasRightToView(obj) || userIsApprover)
								{
									string strObj = obj.get("rawbizObj").ToString();
									if (foundMatch(strObj, bizObj.get("id").ToString()))
									{
										obj.put("approvalSet.approvers", approvers);
										return (approvalData = obj);
									}
								}
							}
						}
					}
				}
				return approvalData;
			}
			catch (Exception e)
			{
				throw new ApprovalException("error executing getApprovalDetails", e);
			}
		}

		protected bool foundMatch(string strObj, string id)
		{
			return strObj.Contains(string.Format("id=\"{0}\"", id));
		}
		public IBizObjList getApprovalData(IApplication app, IRuleContext context, string typeName,Swaf.BGuid.Guid parentId)
		{
			try
			{
				if (app.bizObjMgr.Definitions.Contains(typeName))
				{
					IBizObjList approvalData = app.bizObjMgr.createList(typeName);
					FlexiMap m = new FlexiMap();
					m["typeName"] = typeName;
					m["parentId"] = parentId;
					IBizObjList list = app.appData.get("DA.Approval.NewGetApprovalData", m) as IBizObjList;
					if (list.numElements > 0)
					{
						IBizObjList approvers = getApprovers(app, context, list[0].get("approvalSet") as IBizObj);
						bool userIsPartOfApprover = calcUserIsPartOfApproval(approvers);
						foreach (IBizObj obj in list)
						{
							if (hasRightToView(obj) || userIsPartOfApprover)
							{
								string strObj = obj.get("rawbizObj").ToString();
								IBizObjSource src = app.bizObjMgr.createSource(SourceType.BasicSource, strObj) as IBizObjSource;
								IBizObj approvalBizObj = src.getList(typeName, null)[0];
								if (approvalBizObj != null)
								{
									approvalBizObj.put("override", true);
									if ("2".Equals(obj.get("state").ToString()))
										approvalBizObj.put("override", false);
									approvalBizObj.put("approvalDataId", obj.get("id"));
									approvalData.Add(approvalBizObj);
								}
							}
						}
					}
					return approvalData;
				}
				return null;
			}
			catch (Exception e)
			{
				throw new ApprovalException("Error executing getApprovalData.", e);
			}
		}

		public bool isApproval(IApplication app, IRuleContext context,string typeName,Swaf.BGuid.Guid parentId)
		{
			if (parentId != null)
			{
				FlexiMap m = new FlexiMap();
				m["typeName"] = typeName;
				m["parentId"] = parentId;
				IBizObjList list = app.appData.get("DA.Approval.GetApprovalDataById", m) as IBizObjList;
				return list.numElements > 0;
			}
			return false;
		}
		public IBizObj getApprovalSet(IApplication app,IRuleContext context, string bizobjTypeName, Swaf.BGuid.Guid id)
		{
			FlexiMap m = new FlexiMap();
			m["objectType"] = bizobjTypeName;
			m["id"] = id;
			IBizObjList list = app.appData.get("DA.Approval.GetApprovalSet", m) as IBizObjList;
			if (list.numElements > 0)
				return list[0];
			return null;
		}

		/// <summary>
		/// this rule is called on the preRule execute of feature implementing approval.
		/// a false return value specifies that the rule needs to abort its original call.
		/// when the approval data however has been voted as approved the original rule will
		/// be executed passing in the updated bizObjData.
		/// this rule will also compute if the approval history needs to be persisted or not.
		/// </summary>
		/// <param name="app"></param>
		/// <param name="context"></param>
		/// <param name="originalAction">ruleName</param>
		/// <param name="bizObjData"></param>
		/// <param name="approvalSet"></param>
		/// <returns></returns>
		public bool setupApprovalData(IApplication app, IRuleContext context, string originalAction, IBizObj bizObjData,IBizObj approvalSet)
		{
			try
			{
				bool isApproved = false;
				if (bizObjData != null && bizObjData.containsField("approvalDataId") && approvalSet != null)
				{
					IApprovalManager mngr = app.appData["sysObjs-Approval"] as IApprovalManager;
					IBizObj approvalInfo = mngr.ApprovalInfos.search("id", bizObjData.type.Name);
					
					Swaf.BGuid.Guid nxtApproverId ;
					IResults rc = app.ruleMgr.execute(context,"getNextApprover", approvalSet);

					if (rc.ExceptionInfo != null)
						throw new Exception("errro executing calcNextApprover for setupApprovalData bizrule.", rc.ExceptionInfo);
					
					nxtApproverId = rc.CallResults as Swaf.BGuid.Guid;
					bool isNew = (bizObjData.get("approvalDataId") == null);
					FlexiMap m = new FlexiMap();
					
					m["approvalDataId"] = (isNew) ? new Swaf.BGuid.Guid() : bizObjData.get("approvalDataId");
					m["bizObjData"] = bizObjData.getXml("all setOnly");
					m["approvalSetId"] = approvalSet.get("id");
					m["nextApprover"] = nxtApproverId!=null ? nxtApproverId : CurrentUser.get("id")  ;
					m["originalAction"] = originalAction;
					m["createdBy"] = CurrentUser.get("id");
					m["createdDate"] = DateTime.Now;
					m["typeName"] = bizObjData.type.Name;
					
					if (isNew)
						app.appData.put("DA.Approval.InsertApprovalData", m);
					else
					{
						IBizObjList l = app.appData.get("DA.Approval.GetApprovedData", m) as IBizObjList;
						isApproved = l.numElements > 0 && "1".Equals(l[0].get("state").ToString());
						if (!isApproved)
							app.appData.put("DA.Approval.UpdateApprovalData", m);
					
					}
					if (!isApproved)
					{
						if(context.get("actionType")!=null)
							m["originalAction"] = context.get("actionType");
						if (("True".Equals(approvalSet.get("saveHistory").ToString()) || isNew))
						{
							m["id"] = new Swaf.BGuid.Guid();
							app.appData.put("DA.Approval.InsertApprovalDataChange", m);
						}
						else
							app.appData.put("DA.Approval.UpdateApprovalDataChange", m);
						
					}
					//foreach (IBizObj usr in approvers)
					//    if ((bool)usr.get("notifyForApproval") == true)
					//        app.appData.put("Message.Approval.NotifyApprover", usr);
				}
				return isApproved;
			}
			catch (Exception e)
			{
				throw new ApprovalException("error executing setupApprovalData bizRule.", e);
			}
		}
		
		public void addApprover(IApplication app, IRuleContext context, IBizObj approver,Swaf.BGuid.Guid approvalSetId)
		{
			try
			{
				if (approver != null && approvalSetId != null)
				{
					FlexiMap m = new FlexiMap();
					m["approver"] = approver;
					m["approvalSetId"] = approvalSetId;
					app.appData.put("DA.Approval.InsertApprovalSetApprover", m);
				}
			}
			catch (Exception e)
			{
				throw new ApprovalException("error executing addApprover.", e);
			}
		}

		public void removeApprover(IApplication app, IRuleContext context, IBizObj approvalSet)
		{
		}

		public void createApprovalSet(IApplication app,IRuleContext context,IBizObj approvalDataSet)
		{
			try
			{
				if (approvalDataSet != null)
				{
					app.appData.put("DA.Approval.InsertApprovalSet", approvalDataSet);
					foreach (IBizObj approver in approvalDataSet.get("approvers") as IBizObjList)
					{
						IResults rc = app.ruleMgr.execute(context, "addApprover", approver, approvalDataSet.get("id") as Swaf.BGuid.Guid);
						if (rc.ExceptionInfo != null)
							throw new Exception("Error executing addApprover in addApprovalSet", rc.ExceptionInfo);
					}
				}
			}
			catch (Exception e)
			{
				throw new ApprovalException("error executing addApprovalDataSet.", e);
			}
		}
		public IBizObjList getApproverList(IApplication app, IRuleContext context)
		{
			try
			{
				FlexiMap m = new FlexiMap();
				m["companyId"] = CurrentCompanyId;
				IBizObjList list = app.appData.get("DA.Approval.GetCompanyApprovers", m) as IBizObjList;
				return list;
			}
			catch (Exception e)
			{
				throw new ApprovalException("error executing getAproverList",e);
			}
			
		}
		
		
		protected bool calcUserIsPartOfApproval(IBizObjList list)
		{
			if (CurrentUser != null && list!=null)
				return (list.search("id", CurrentUser.get("id")) != null);
			return false;
		}
		protected bool hasRightToView(IBizObj obj)
		{
			//todo: 
			if (obj.get("lastUpdateBy.id").Equals(CurrentUser.get("id")) || obj.get("createdById").Equals(CurrentUser.get("id")))
				return true;
			return false;
			
		}

		public bool hasVoted(IApplication app, IRuleContext context,Swaf.BGuid.Guid approvalDataId)
		{
			try
			{
				FlexiMap m = new FlexiMap();
				m["id"] = approvalDataId;
				m["userId"] = CurrentUser.get("id");
				IBizObjList list = app.appData.get("DA.Approval.hasVoted", m) as IBizObjList;
				if (list.numElements > 0)
					return true;
				return false;
			}
			catch (Exception e)
			{
				throw new ApprovalException("error executing hasVoted", e);
			}
		}
		public IBizObjList getBizObjApprovers(IApplication app, IRuleContext context, string bizobjTypeName, Swaf.BGuid.Guid id)
		{
			IBizObjList list = App.bizObjMgr.createList("Approver");
			IResults rc = app.ruleMgr.execute("getApprovalSet",bizobjTypeName,id);
			IBizObj set = rc.CallResults as IBizObj;
			if (set != null)
			{
				list= app.appData.get("DA.Approval.GetApprovers", set) as IBizObjList;
				return list;
			}
			return list;
		}
		public IBizObjList getApprovers(IApplication app, IRuleContext context,IBizObj approvalSet)
		{
			IBizObjList approvers = app.bizObjMgr.createList("Approver");
			if (approvalSet != null)
			{
				IBizObjList list = app.appData.get("DA.Approval.GetApprovalSetTree", approvalSet) as IBizObjList;
				if (list.numElements > 0)
					foreach (IBizObj obj in list)
					{
						IBizObjList l = obj.get("approvers") as IBizObjList;
						foreach (IBizObj approver in l)
							approvers.Add(approver);
					}
			} return approvers;
		}

		public Swaf.BGuid.Guid getNextApprover(IApplication app, IRuleContext context, IBizObj approvalSet)
		{
			if (approvalSet != null )
			{
				IBizObjList approvers = getApprovers(app, context, approvalSet);
				if (approvers != null && approvers.numElements > 0)
				{
					IBizObj currentUser = approvers.search("id", CurrentUser.get("id"));
					if (currentUser == null)
						return approvers[0].get("id") as Swaf.BGuid.Guid;
					else
						foreach (IBizObj user in approvers.getIndex("rank"))
							if (Convert.ToInt16(user.get("rank")) < Convert.ToInt16(currentUser.get("rank")))
								return user.get("id") as Swaf.BGuid.Guid;
				}
			}
			return null;
		}
		
	}
}
